#!/usr/bin/env python3
"""
TDOS命令行界面程序
基于TDOS文件系统库的简易命令行界面
支持磁盘创建、登录、列出、删除和挂载功能
"""

import os
import sys
import argparse
import cmd
import re
import shutil
import json
from pathlib import Path

# 导入TDOS库
try:
    from to import TDOS, TDOSError, tdos_strerror
except ImportError:
    print("错误: 无法导入TDOS库(to.py)")
    print("请确保to.py文件在当前目录或PYTHONPATH中")
    sys.exit(1)

class TDOSShell(cmd.Cmd):
    """TDOS命令行界面"""
    
    intro = "欢迎使用TDOS命令行界面。输入 'help' 获取帮助信息。"
    prompt = "TDOS> "
    
    def __init__(self):
        super().__init__()
        self.tdos = TDOS()
        self.current_disk = None
        self.current_path = "/"
        self.disk_list = {}
        self.mount_points = {}  # 挂载点信息: {挂载点路径: (磁盘名称, 磁盘ID, 磁盘路径)}
        self.disk_dir = "disk"
        self.disk_file = "diskfile.cnx"
        
        # 初始化磁盘目录
        self._init_disk_directory()
        
        # 加载磁盘列表和挂载信息
        self._load_disk_list()
        self._load_mount_info()
    
    def _init_disk_directory(self):
        """初始化磁盘目录"""
        if not os.path.exists(self.disk_dir):
            os.makedirs(self.disk_dir)
        
        # 切换到磁盘目录
        os.chdir(self.disk_dir)
    
    def _load_disk_list(self):
        """加载磁盘列表"""
        disk_list_path = os.path.join("..", self.disk_file)
        
        if os.path.exists(disk_list_path):
            try:
                with open(disk_list_path, 'r') as f:
                    data = json.load(f)
                    self.disk_list = data.get('disks', {})
                    self.mount_points = data.get('mounts', {})
            except (json.JSONDecodeError, IOError):
                # 如果JSON解析失败，尝试旧的文本格式
                self._load_old_disk_format(disk_list_path)
        
        # 扫描磁盘目录中的磁盘
        self._scan_disks()
    
    def _load_old_disk_format(self, disk_list_path):
        """加载旧的磁盘列表格式"""
        try:
            with open(disk_list_path, 'r') as f:
                for line in f:
                    line = line.strip()
                    if line and not line.startswith('#'):
                        parts = line.split(':')
                        if len(parts) >= 2:
                            disk_name = parts[0]
                            disk_id = parts[1]
                            disk_path = parts[2] if len(parts) > 2 else disk_name
                            self.disk_list[disk_name] = (disk_id, disk_path)
        except IOError:
            print(f"警告: 无法读取磁盘列表文件 {disk_list_path}")
    
    def _scan_disks(self):
        """扫描磁盘目录中的磁盘"""
        for item in os.listdir('.'):
            if os.path.isdir(item):
                bios_path = os.path.join(item, "S.BIOS.tdos")
                if os.path.exists(bios_path):
                    try:
                        with open(bios_path, 'r') as f:
                            content = f.read()
                            disk_id = None
                            disk_name = None
                            
                            for line in content.split('\n'):
                                if line.startswith('disk_id:'):
                                    disk_id = line[8:].strip()
                                elif line.startswith('disk_name:'):
                                    disk_name = line[10:].strip()
                            
                            if disk_id and disk_name and disk_name not in self.disk_list:
                                self.disk_list[disk_name] = (disk_id, item)
                    except IOError:
                        print(f"警告: 无法读取磁盘BIOS文件 {bios_path}")
        
        # 保存更新后的磁盘列表
        self._save_disk_list()
    
    def _load_mount_info(self):
        """加载挂载信息"""
        mount_file = os.path.join("..", "mounts.cnx")
        if os.path.exists(mount_file):
            try:
                with open(mount_file, 'r') as f:
                    self.mount_points = json.load(f)
            except (json.JSONDecodeError, IOError):
                print("警告: 无法读取挂载信息文件")
                self.mount_points = {}
    
    def _save_disk_list(self):
        """保存磁盘列表"""
        disk_list_path = os.path.join("..", self.disk_file)
        
        try:
            with open(disk_list_path, 'w') as f:
                data = {
                    'disks': self.disk_list,
                    'mounts': self.mount_points
                }
                json.dump(data, f, indent=2)
        except IOError:
            print(f"错误: 无法保存磁盘列表文件 {disk_list_path}")
    
    def _save_mount_info(self):
        """保存挂载信息"""
        mount_file = os.path.join("..", "mounts.cnx")
        try:
            with open(mount_file, 'w') as f:
                json.dump(self.mount_points, f, indent=2)
        except IOError:
            print("错误: 无法保存挂载信息文件")
    
    def do_login(self, arg):
        """
        登录到TDOS磁盘
        
        用法:
          login <磁盘名称>:<磁盘ID>   # 登录到指定磁盘
          login -list                 # 列出所有可用磁盘
          login -c <磁盘名称>         # 创建新磁盘
          login -d <磁盘名称>         # 删除磁盘
        """
        args = arg.split()
        
        if not args:
            print("错误: 需要指定参数")
            return
        
        if args[0] == "-list":
            self._list_disks()
            return
        
        if args[0] == "-c":
            if len(args) < 2:
                print("错误: 需要指定磁盘名称")
                return
            
            disk_name = args[1]
            self._create_disk(disk_name)
            return
        
        if args[0] == "-d":
            if len(args) < 2:
                print("错误: 需要指定磁盘名称")
                return
            
            disk_name = args[1]
            self._delete_disk(disk_name)
            return
        
        # 常规登录
        login_arg = args[0]
        if ':' not in login_arg:
            print("错误: 登录参数格式应为'磁盘名称:磁盘ID'")
            return
        
        disk_name, disk_id = login_arg.split(':', 1)
        
        if disk_name not in self.disk_list:
            print(f"错误: 磁盘 '{disk_name}' 不存在")
            return
        
        stored_disk_id, disk_path = self.disk_list[disk_name]
        if stored_disk_id != disk_id:
            print(f"错误: 磁盘ID不匹配")
            print(f"提供的ID: {disk_id}")
            print(f"实际ID: {stored_disk_id}")
            return
        
        # 如果当前已登录到某个磁盘，先退出
        if self.current_disk:
            print(f"已从磁盘 '{self.current_disk}' 退出")
            self.current_disk = None
            self.prompt = "TDOS> "
        
        # 加载磁盘
        err = self.tdos.loaddisk(disk_path, disk_name)
        if err != TDOSError.SUCCESS:
            print(f"错误: 无法加载磁盘 '{disk_name}': {tdos_strerror(err)}")
            return
        
        self.current_disk = disk_name
        self.current_path = "/"
        self.prompt = f"TDOS({disk_name})> "
        print(f"已登录到磁盘: {disk_name}")
    
    def _list_disks(self):
        """列出所有可用磁盘"""
        if not self.disk_list:
            print("没有可用的磁盘")
            return
        
        print("可用磁盘:")
        for disk_name, (disk_id, disk_path) in self.disk_list.items():
            status = " (已挂载)" if any(mount[0] == disk_name for mount in self.mount_points.values()) else ""
            print(f"  {disk_name}:{disk_id} ({disk_path}){status}")
    
    def _create_disk(self, disk_name):
        """创建新磁盘"""
        if disk_name in self.disk_list:
            print(f"错误: 磁盘 '{disk_name}' 已存在")
            return
        
        # 创建磁盘目录
        disk_path = disk_name
        if os.path.exists(disk_path):
            print(f"错误: 磁盘目录 '{disk_path}' 已存在")
            return
        
        os.makedirs(disk_path)
        
        # 创建磁盘
        err = self.tdos.loaddisk(disk_path, disk_name)
        if err != TDOSError.SUCCESS:
            print(f"错误: 无法创建磁盘 '{disk_name}': {tdos_strerror(err)}")
            # 清理创建的目录
            shutil.rmtree(disk_path)
            return
        
        # 保存磁盘
        err = self.tdos.savedisk()
        if err != TDOSError.SUCCESS:
            print(f"错误: 无法保存磁盘 '{disk_name}': {tdos_strerror(err)}")
            # 清理创建的目录
            shutil.rmtree(disk_path)
            return
        
        # 获取磁盘ID
        disk_id = self.tdos.handle.bios_data.disk_id if self.tdos.handle else "未知"
        
        # 添加到磁盘列表
        self.disk_list[disk_name] = (disk_id, disk_path)
        self._save_disk_list()
        
        print(f"已创建磁盘: {disk_name}:{disk_id}")
    
    def _delete_disk(self, disk_name):
        """删除磁盘"""
        if disk_name not in self.disk_list:
            print(f"错误: 磁盘 '{disk_name}' 不存在")
            return
        
        # 检查是否当前正在使用的磁盘
        if self.current_disk == disk_name:
            print(f"警告: 不能删除当前正在使用的磁盘 '{disk_name}'")
            print("请先使用 'logout' 命令退出当前磁盘")
            return
        
        # 检查是否被挂载
        mounted_paths = []
        for mount_path, (mounted_disk, _, _) in self.mount_points.items():
            if mounted_disk == disk_name:
                mounted_paths.append(mount_path)
        
        if mounted_paths:
            print(f"错误: 磁盘 '{disk_name}' 正在被挂载，无法删除")
            print("挂载点:", ", ".join(mounted_paths))
            print("请先使用 'unmount' 命令卸载这些挂载点")
            return
        
        # 确认删除
        confirm = input(f"确定要删除磁盘 '{disk_name}' 吗？此操作不可恢复！(y/N): ")
        if confirm.lower() != 'y':
            print("取消删除操作")
            return
        
        # 获取磁盘路径
        disk_id, disk_path = self.disk_list[disk_name]
        
        # 删除磁盘目录
        try:
            shutil.rmtree(disk_path)
            print(f"已删除磁盘目录: {disk_path}")
        except OSError as e:
            print(f"错误: 无法删除磁盘目录 '{disk_path}': {e}")
            return
        
        # 从磁盘列表中移除
        del self.disk_list[disk_name]
        self._save_disk_list()
        
        print(f"已删除磁盘: {disk_name}")
    
    def do_logout(self, arg):
        """
        退出当前磁盘
        
        用法:
          logout
        """
        if not self.current_disk:
            print("当前未登录任何磁盘")
            return
        
        print(f"已从磁盘 '{self.current_disk}' 退出")
        self.current_disk = None
        self.current_path = "/"
        self.prompt = "TDOS> "
    
    def do_mount(self, arg):
        """
        挂载磁盘到指定挂载点
        
        用法:
          mount <挂载点路径> <磁盘名称>:<磁盘ID>
        """
        if not self._check_disk_loaded():
            return
        
        args = arg.split()
        if len(args) < 2:
            print("错误: 需要指定挂载点路径和磁盘名称:磁盘ID")
            return
        
        mount_point = args[0]
        disk_arg = args[1]
        
        if ':' not in disk_arg:
            print("错误: 磁盘参数格式应为'磁盘名称:磁盘ID'")
            return
        
        disk_name, disk_id = disk_arg.split(':', 1)
        
        if disk_name not in self.disk_list:
            print(f"错误: 磁盘 '{disk_name}' 不存在")
            return
        
        stored_disk_id, disk_path = self.disk_list[disk_name]
        if stored_disk_id != disk_id:
            print(f"错误: 磁盘ID不匹配")
            print(f"提供的ID: {disk_id}")
            print(f"实际ID: {stored_disk_id}")
            return
        
        # 检查挂载点是否已存在
        if mount_point in self.mount_points:
            print(f"错误: 挂载点 '{mount_point}' 已存在")
            return
        
        # 添加到挂载点列表
        self.mount_points[mount_point] = (disk_name, disk_id, disk_path)
        self._save_mount_info()
        self._save_disk_list()
        
        print(f"已挂载磁盘 '{disk_name}' 到 '{mount_point}'")
    
    def do_unmount(self, arg):
        """
        卸载挂载点
        
        用法:
          unmount <挂载点路径>
        """
        if not arg:
            print("错误: 需要指定挂载点路径")
            return
        
        if arg not in self.mount_points:
            print(f"错误: 挂载点 '{arg}' 不存在")
            return
        
        # 从挂载点列表中移除
        disk_name, disk_id, disk_path = self.mount_points[arg]
        del self.mount_points[arg]
        self._save_mount_info()
        self._save_disk_list()
        
        print(f"已卸载挂载点 '{arg}' (磁盘: {disk_name})")
    
    def do_mounts(self, arg):
        """
        列出所有挂载点
        
        用法:
          mounts
        """
        if not self.mount_points:
            print("没有挂载点")
            return
        
        print("挂载点列表:")
        for mount_point, (disk_name, disk_id, disk_path) in self.mount_points.items():
            print(f"  {mount_point} -> {disk_name}:{disk_id} ({disk_path})")
    
    def do_ls(self, arg):
        """
        列出目录内容
        
        用法:
          ls [路径]
        """
        if not self._check_disk_loaded():
            return
        
        path = arg if arg else self.current_path
        
        err, content = self.tdos.os(path, 'r')
        if err != TDOSError.SUCCESS:
            print(f"错误: {tdos_strerror(err)}")
            return
        
        # 按行分割并过滤空行
        lines = content.split('\n')
        for line in lines:
            if line.strip():  # 非空行
                print(line)
    
    def do_cd(self, arg):
        """
        更改当前目录
        
        用法:
          cd [路径]
        """
        if not self._check_disk_loaded():
            return
        
        if not arg:
            # 切换到根目录
            self.current_path = "/"
            return
        
        target_path = arg
        
        # 处理相对路径
        if not target_path.startswith('/'):
            if self.current_path == '/':
                target_path = '/' + target_path
            else:
                target_path = self.current_path + '/' + target_path
        
        # 规范化路径
        path_parts = [p for p in target_path.split('/') if p]
        
        # 处理路径中的 . 和 ..
        normalized_parts = []
        for part in path_parts:
            if part == '.':
                continue
            elif part == '..':
                if normalized_parts:
                    normalized_parts.pop()
            else:
                normalized_parts.append(part)
        
        # 重建路径
        normalized_path = '/' + '/'.join(normalized_parts)
        if normalized_path == '':
            normalized_path = '/'
        
        # 检查路径是否存在
        err, content = self.tdos.os(normalized_path, 'r')
        if err != TDOSError.SUCCESS:
            print(f"错误: {tdos_strerror(err)}")
            return
        
        self.current_path = normalized_path
    
    def do_pwd(self, arg):
        """
        打印当前工作目录
        
        用法:
          pwd
        """
        if not self._check_disk_loaded():
            return
        
        print(self.current_path)
    
    def do_mkdir(self, arg):
        """
        创建目录
        
        用法:
          mkdir <目录名>
        """
        if not self._check_disk_loaded():
            return
        
        if not arg:
            print("错误: 需要指定目录名")
            return
        
        # 构建完整路径
        if self.current_path == '/':
            full_path = '/' + arg
        else:
            full_path = self.current_path + '/' + arg
        
        # 创建目录
        err, _ = self.tdos.os(full_path, 'w', None)
        if err != TDOSError.SUCCESS:
            print(f"错误: {tdos_strerror(err)}")
            return
        
        print(f"已创建目录: {arg}")
    
    def do_touch(self, arg):
        """
        创建空文件
        
        用法:
          touch <文件名>
        """
        if not self._check_disk_loaded():
            return
        
        if not arg:
            print("错误: 需要指定文件名")
            return
        
        # 构建完整路径
        if self.current_path == '/':
            full_path = '/' + arg
        else:
            full_path = self.current_path + '/' + arg
        
        # 创建空文件
        err, _ = self.tdos.os(full_path, 'w', "")
        if err != TDOSError.SUCCESS:
            print(f"错误: {tdos_strerror(err)}")
            return
        
        print(f"已创建文件: {arg}")
    
    def do_cat(self, arg):
        """
        显示文件内容
        
        用法:
          cat <文件名>
        """
        if not self._check_disk_loaded():
            return
        
        if not arg:
            print("错误: 需要指定文件名")
            return
        
        # 构建完整路径
        if self.current_path == '/':
            full_path = '/' + arg
        else:
            full_path = self.current_path + '/' + arg
        
        # 读取文件内容
        err, content = self.tdos.os(full_path, 'r')
        if err != TDOSError.SUCCESS:
            print(f"错误: {tdos_strerror(err)}")
            return
        
        print(content, end='')
    
    def do_echo(self, arg):
        """
        写入内容到文件
        
        用法:
          echo "内容" > <文件名>
        """
        if not self._check_disk_loaded():
            return
        
        # 解析参数
        match = re.match(r'^"([^"]*)"\s*>\s*(\S+)$', arg)
        if not match:
            print("错误: 参数格式应为: \"内容\" > 文件名")
            return
        
        content = match.group(1)
        filename = match.group(2)
        
        # 构建完整路径
        if self.current_path == '/':
            full_path = '/' + filename
        else:
            full_path = self.current_path + '/' + filename
        
        # 写入文件
        err, _ = self.tdos.os(full_path, 'w', content)
        if err != TDOSError.SUCCESS:
            print(f"错误: {tdos_strerror(err)}")
            return
        
        print(f"已写入内容到文件: {filename}")
    
    def do_rm(self, arg):
        """
        删除文件
        
        用法:
          rm <文件名>
        """
        if not self._check_disk_loaded():
            return
        
        if not arg:
            print("错误: 需要指定文件名")
            return
        
        # 构建完整路径
        if self.current_path == '/':
            full_path = '/' + arg
        else:
            full_path = self.current_path + '/' + arg
        
        # 删除文件
        err, _ = self.tdos.os(full_path, 'm')
        if err != TDOSError.SUCCESS:
            print(f"错误: {tdos_strerror(err)}")
            return
        
        print(f"已删除文件: {arg}")
    
    def do_rm_rf(self, arg):
        """
        递归删除目录及其内容
        
        用法:
          rm -rf <目录名>
        """
        if not self._check_disk_loaded():
            return
        
        if not arg:
            print("错误: 需要指定目录名")
            return
        
        # 构建完整路径
        if self.current_path == '/':
            full_path = '/' + arg
        else:
            full_path = self.current_path + '/' + arg
        
        # 检查路径是否存在
        err, content = self.tdos.os(full_path, 'r')
        if err != TDOSError.SUCCESS:
            print(f"错误: 目录 '{arg}' 不存在")
            return
        
        # 确认删除
        confirm = input(f"确定要递归删除目录 '{arg}' 吗？此操作不可恢复！(y/N): ")
        if confirm.lower() != 'y':
            print("取消删除操作")
            return
        
        # 递归删除目录
        self._rm_rf(full_path)
    
    def _rm_rf(self, path):
        """递归删除目录"""
        # 首先列出目录内容
        err, content = self.tdos.os(path, 'r')
        if err != TDOSError.SUCCESS:
            print(f"错误: 无法访问 {path}: {tdos_strerror(err)}")
            return
        
        # 处理目录内容
        lines = content.split('\n')
        for line in lines:
            line = line.strip()
            if not line or line in ('.', '..'):
                continue
            
            # 判断是文件还是目录
            is_dir = line.endswith('/')
            is_link = line.endswith('@')
            name = line[:-1] if is_dir or is_link else line
            
            # 构建子路径
            if path == '/':
                child_path = '/' + name
            else:
                child_path = path + '/' + name
            
            if is_dir:
                # 递归删除子目录
                self._rm_rf(child_path)
            else:
                # 删除文件
                err, _ = self.tdos.os(child_path, 'm')
                if err != TDOSError.SUCCESS:
                    print(f"警告: 无法删除文件 {child_path}: {tdos_strerror(err)}")
        
        # 删除目录本身
        if path != '/':  # 不能删除根目录
            err, _ = self.tdos.os(path, 'm')
            if err != TDOSError.SUCCESS:
                print(f"警告: 无法删除目录 {path}: {tdos_strerror(err)}")
            else:
                print(f"已删除目录: {path}")
    
    def do_lscpu(self, arg):
        """
        显示磁盘BIOS信息
        
        用法:
          lscpu
        """
        if not self._check_disk_loaded():
            return
        
        if not self.tdos.handle:
            print("错误: 磁盘未正确加载")
            return
        
        bios = self.tdos.handle.bios_data
        print(f"磁盘名称: {bios.disk_name}")
        print(f"磁盘ID: {bios.disk_id}")
        print(f"算法路径: {bios.algorithm_path}")
        print(f"根路径: {bios.root_path}")
        print(f"下一个块ID: {bios.next_block_id}")
        print(f"下一个局部ID: {bios.next_local_id}")
    
    def do_exit(self, arg):
        """
        退出TDOS命令行
        
        用法:
          exit
        """
        print("再见!")
        return True
    
    def do_quit(self, arg):
        """退出TDOS命令行 (exit的别名)"""
        return self.do_exit(arg)
    
    def _check_disk_loaded(self):
        """检查是否有磁盘加载"""
        if not self.current_disk:
            print("错误: 未加载任何磁盘，请先使用 'login' 命令登录磁盘")
            return False
        return True

def main():
    """主函数"""
    parser = argparse.ArgumentParser(description="TDOS命令行界面")
    parser.add_argument("-d", "--disk-dir", default="disk", help="磁盘目录路径")
    parser.add_argument("-f", "--disk-file", default="diskfile.cnx", help="磁盘列表文件路径")
    
    args = parser.parse_args()
    
    # 创建并启动shell
    shell = TDOSShell()
    shell.disk_dir = args.disk_dir
    shell.disk_file = args.disk_file
    
    # 重新初始化磁盘目录
    shell._init_disk_directory()
    shell._load_disk_list()
    
    shell.cmdloop()

if __name__ == "__main__":
    main()
